package 
{
	/**
	* ...
	* @author Exey Panteleev
	* Math by Yamasv http://yamasv.blog92.fc2.com/blog-entry-85.html
	*/
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import net.hires.utils.Stats;
	import away3d.core.scene.View3D;
	import away3d.core.math.Number3D;
	import away3d.core.material.WireColorMaterial;
	import away3d.objects.LineSegment;
	import away3d.objects.Plane;
	import away3d.objects.Sphere;
	
    [SWF(backgroundColor="#222015", frameRate="60", width="600", height="400")]
    public class Fluid2 extends Sprite
    {
			//Math
            private var theta:Number = 0;
            private var buffer:Array = new Array();
            private var renderBuffer:int = 0;
            private var k1:Number;
            private var k2:Number;
            private var k3:Number;
			
			//Common
			private var range:int = 20;
			private var waterSegsX:int = range;
			private var waterSegsY:int = range;
			private var waterSegsXreal:int = waterSegsX+1;
			private var waterSegsYreal:int = waterSegsY+1;
			private var waterXcenter:int = waterSegsX/2;
			private var waterYcenter:int= waterSegsY/2;
			private var isPause:Boolean = false;

			//Away3D
			public var view:View3D;
			private var waterSurface:Plane;
			private var waterMat:WireColorMaterial;
			
        public function Fluid2()
        {
			prepareAway3d();
			prepareMath();
			createObjects();
			addListeners();
        }
		
		private function prepareAway3d() {
			stage.quality   = "LOW";
			stage.scaleMode = "noScale";
			stage.align = StageAlign.TOP_LEFT;
            view = new View3D();
            view.x = 300;
            view.y = 200;
            addChild(view);
			view.camera.z = 1800;
			stage.addChild( new Stats() ); // MrDoob Stats http://code.google.com/p/mrdoob/
		}
		private function changeWaterMaterial(matIndex:uint) {
			var material1:WireColorMaterial;
			switch(matIndex) {
				case 1:
					material1 = new WireColorMaterial(0x0000ff, {wirecolor: 0x9b85f9});
					waterSurface.material = material1;
					makeSplash(30);
				break;
			}
		}
		private function createObjects() {
			waterSurface = new Plane( { width:1200, height:1200, segmentsW:waterSegsX, segmentsH:waterSegsY } );
			changeWaterMaterial(1);
			//waterSurface.rotationX = -90;
			waterSurface.back = waterMat;
			view.scene.addChild(waterSurface);
			trace("vertices: ", waterSurface.vertices.length, "faces: ", waterSurface.faces.length)
		}		
		private function addListeners() {
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
		}
		
		public function prepareMath() {
			populateBuffer();
			trace(buffer[0].length, buffer[1].length)
			var d:Number = 1;
			var t:Number = 0.1;
			var c:Number = 2.1;
			var mu:Number = 0.001;

			var f1:Number = c*c*t*t/(d*d);
			var f2:Number = 1/(mu*t+2);

			k1 = (4 - 8*f1)*f2;
			k2 = (mu*t-2)*f2;
			k3 = 2 * f1 * f2;
		}
		
		public function populateBuffer() {
			var x:int,y:int,z:int;
			// 3-d displacement will be stored in the array(2*w*h)
			for(var i:int = 0 ; i < 2; i++){
				var a:Array = new Array();
				for(var j:int = 0 ; j < waterSegsYreal; j++){
					var aa:Array = new Array();
					for(var k:int = 0 ; k < waterSegsXreal; k++)
						aa.push(new Number3D(j,0,k));
					a.push(aa);
				}
				buffer[i] = a;
			}
		}
		
		public function makeSplash(splashHeight:int) {
			populateBuffer();
			buffer[renderBuffer][waterXcenter][waterYcenter].y = splashHeight;	
		}
		
		private function evaluate():void{
			for(var j:int = 1 ; j < waterSegsYreal-1; j++){
				var crnt:Array = buffer[renderBuffer][j];
				var prev:Array = buffer[1-renderBuffer][j];
				for (var i:int = 1 ; i < waterSegsXreal-1; i++) {
				//	trace(j, i);
					var currentN:Number3D = (Number3D)(buffer[renderBuffer][j + 1][i]);
					var currentP:Number3D = (Number3D)(buffer[renderBuffer][j - 1][i]);
					((Number3D)(prev[i])).y = 
						k1*((Number3D)(crnt[i])).y + k2*((Number3D)(prev[i])).y + 
						k3*(((Number3D)(crnt[i+1])).y + ((Number3D)(crnt[i-1])).y
								+ currentN.y + currentP.y);
				}
			}
			renderBuffer = 1-renderBuffer;
		}
		

        private function onEnterFrame(event:Event):void
        {
			//light.copyPosition(camera);
               // trace( buffer[renderBuffer][1][0]);
            if(!isPause){
				theta += 0.05;
				var verCounter:uint = 0;
                for(var x:int = 0 ; x < waterSegsXreal ; x++){
                    for(var z:int = 0 ; z < waterSegsYreal ; z++){
 						waterSurface.vertices[verCounter].y = buffer[renderBuffer][x][z].y * 3;
						verCounter++;
                    }
                }
				evaluate();
			}
            rotateCamera();				
            view.render();
        }

		private function rotateCamera():void{
			// Move camera based on mouse position
			var x:Number = (stage.mouseX - stage.stageWidth/2);
			view.camera.moveLeft(x);
			var y:Number = (stage.mouseY - stage.stageHeight/2);
			view.camera.moveDown(y);
			if (view.camera.y > 1000 || view.camera.y < -200) view.camera.moveDown(-y);
			view.camera.lookAt(waterSurface.position);
		}
		
		public function keyUpHandler(e:KeyboardEvent) {
			if (isChar(e.charCode, "1")) 
				changeWaterMaterial(1);
			else if (isChar(e.charCode, "0")) 
				if(isPause == false)
					isPause = true;
				else
					isPause = false;
		}
		
		private function isChar(code:Number, str:String){
			if(code == str.charCodeAt())
				return true;
			return false;
		}
    }
}